 #ifndef _game_Actions_hpp_
 #define _game_Actions_hpp_

/** \file Actions.hpp
*  \author Adrian Schweizer
*  \created  $Sa 18 Aug 03:48:01 pm CEST 2007 schwadri@SchwadriComp.local$
*  \modified $Mo 11 Feb 09:21:25 pm CET 2008 schwadri@SchwadriLaptop.local$
*/
#include <limits>

#include <boost/preprocessor/seq.hpp>
#include <boost/preprocessor/cat.hpp>

#include <boost/mpl/vector.hpp>

#include <boost/serialization/access.hpp>
#include <boost/serialization/split_member.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/base_object.hpp>

#include "Common.hpp"
#include "../network/Types.hpp"
#include "../network/Client.hpp"
#include "Chat.hpp"
#include "Player.hpp"
#include "GameOptions.hpp"


namespace game {

    //Forward Decl
    class GameController;

    namespace action {

        using network::receiver_mask;

        /** \class GameAction A reference game action looks like this:
         *  \code
         *  class GameAction
         *  {
         *  public:
         *      //1. provides Standard constructor
         *      GameAction();
         *
         *      //2. Provides Validate method with the following signature
         *      bool    Validate(const GameController& gc, network::Client::Id source) const;
         *
         *      //3. Provides Execute method with signature:
         *      bool    Execute(GameController& game_controller, network::Client::Id source) const;
         *
         *      //4. provides a private serialization template method and access for boost::serialization
         *  private:
         *      friend class boost::serialization::access;
         *      template<class Archive>
         *          void    serialize(Archive & ar, const int unsigned version)
         *          {
         *              ar & some_member_variable;
         *              //....
         *          }
         *  };
         *  \endcode
         *  \todo A flexible mechanism is needed for actions to be able to specify to which clients they should be sent
         */

        #define ACTION_TYPE_FORWARD_DECL(r,data,TYPE)   \
                class TYPE;

        #define ACTION_TYPE_VECTOR(TYPES,NAME)                                  \
            BOOST_PP_SEQ_FOR_EACH(CORE_DETAIL_VECTOR_TYPE_FORWARD_DECL, ,TYPES) \
            typedef boost::mpl::BOOST_PP_CAT(vector,BOOST_PP_SEQ_SIZE(TYPES))<  \
            BOOST_PP_SEQ_ENUM(TYPES) > NAME;

        /** \brief the game action list*/
        ACTION_TYPE_VECTOR(
                                (ClientChangeName)
                                (ClientChangeTeam)    // if spec->becomes player, if player and invalid team->becomes spec
                                (PlayerReady)
                                (TeamChangeName)
                                (TeamChangeOpenSlots)
                                (GameChangeOptions)
                                (GameChangeName)
                                //(ChatMessage)
                                ,GameActions)

        #undef ACTION_TYPE_VECTOR
        #undef ACTION_TYPE_FORWARD_DECL

        //FIXME:stupid doxygen f*ck's up the documentation

        /** \brief Contains common utility functionality for Actions
         *
         */
        struct  GameActionBase
        {
            /** \brief default ctor
             *
             *  the bit mask of action receivers is initialized with \a RECEIVER_ALL
             */
            GameActionBase()
            { }

            /** \brief ctor
             *
             *  \param[in] r a bit mask of clients that should receive the action.
             */
            GameActionBase(const receiver_mask& r)
            :   m_receivers(r)
            { }

            /** \brief validates the \a clientid of the action origin
             *
             *  \param[in] gc       reference to the GameController
             *  \param[in] origin   id of the originator of this action
             *  \param[in] sender   id of the client who sent you that action
             *  \return
             *  -   \a true if the clientid is in a valid range and the Client Object refered by this id is
             *      marked \a valid too. Further a check is made whether the \a sender is allowed to
             *      send actions that come actually from \a origin (only network::HOST or network::SERVER may send
             *      actions that originate from another client than themselves)
             *  -   \a false otherwise
             */
            bool    ValidateOrigin(GameController& gc, network::Client::Id origin, network::Client::Id sender) const;

            const network::receiver_mask&  Receivers() const   { return m_receivers; }
        protected:
            network::receiver_mask  m_receivers;    ///< receiver bit mask
        };


        class ClientChangeName : public GameActionBase
        {
        public:
            ClientChangeName();
            ClientChangeName(network::Client::Id cid, const std::string& name, const receiver_mask& r = receiver_mask());

            bool Validate(GameController& gc, network::Client::Id sender) const;
            bool Execute(GameController& game_controller, network::Client::Id sender) const;


        private:
            //BOOST_SERIALIZATION_SPLIT_MEMBER()
            friend class boost::serialization::access;
            template<class Archive>
                void    serialize(Archive & ar, const int unsigned version)
                {
                    ar & m_cid;
                    ar & m_name;
                }

            network::Client::Id m_cid;
            std::string         m_name;
        };

        /*class  ChatMessage : public GameActionBase
        {
        public:
            ChatMessage();
            ChatMessage(network::Client::Id origin, char channel, network::Client::Id target, const std::string& text);

            bool Validate(GameController& gc, network::Client::Id sender) const;
            bool Execute(GameController& game_controller, network::Client::Id sender) const;

        private:
            //BOOST_SERIALIZATION_SPLIT_MEMBER()
            friend class boost::serialization::access;
            template<class Archive>
                void    serialize(Archive & ar, const int unsigned version)
                {
                    ar & m_message;
                }

            Chat::Message   m_message;
        };*/

        class  ClientChangeTeam : public GameActionBase
        {
        public:
            ClientChangeTeam();
            ClientChangeTeam(network::Client::Id origin, char new_team, const receiver_mask& r = receiver_mask());

            bool    Validate(GameController& gc, network::Client::Id sender) const;
            bool    Execute(GameController& game_controller, network::Client::Id sender) const;

        private:
            bool    ValidateTeamId(GameController& game_controller,char teamid) const;
            //BOOST_SERIALIZATION_SPLIT_MEMBER()
            friend class boost::serialization::access;
            template<class Archive>
                void    serialize(Archive & ar, const int unsigned version)
                {
                    ar & m_cid;
                    ar & m_teamid;
                }
            network::Client::Id   m_cid;
            char                m_teamid;
        };

        class  PlayerReady : public GameActionBase
        {
        public:
            PlayerReady();
            PlayerReady(Player::Id pid, int ticks, const receiver_mask& r = receiver_mask());

            bool    Validate(GameController& gc, network::Client::Id sender) const;
            bool    Execute(GameController& game_controller, network::Client::Id sender);

        private:
            //BOOST_SERIALIZATION_SPLIT_MEMBER()
            friend class boost::serialization::access;
            template<class Archive>
                void    serialize(Archive & ar, const int unsigned version)
                {
                    ar & m_pid;
                    ar & m_ticks;
                }
            Player::Id  m_pid;
            int         m_ticks;
        };

        class GameChangeOptions : public GameActionBase
        {
        public:
            GameChangeOptions();
            GameChangeOptions(const GameOptions& go, const receiver_mask& r = receiver_mask());

            bool    Validate(GameController& gc, network::Client::Id sender) const;
            bool    Execute(GameController& game_controller, network::Client::Id sender) const;

        private:
            //BOOST_SERIALIZATION_SPLIT_MEMBER()
            friend class boost::serialization::access;
            template<class Archive>
                void    serialize(Archive & ar, const int unsigned version)
                {
                    ar & m_go;
                }
            GameOptions            m_go;
        };

        class GameChangeName : public GameActionBase
        {
        public:
            GameChangeName();
            GameChangeName(const std::string& name, const receiver_mask& r = receiver_mask());

            bool    Validate(GameController& gc, network::Client::Id sender) const;
            bool    Execute(GameController& game_controller, network::Client::Id sender) const;
        private:
            //BOOST_SERIALIZATION_SPLIT_MEMBER()
            friend class boost::serialization::access;
            template<class Archive>
                void    serialize(Archive & ar, const int unsigned version)
                {
                    ar & m_game_name;
                }

            std::string            m_game_name;
        };

        class TeamChangeName : public GameActionBase
        {
        public:
            TeamChangeName();
            TeamChangeName(char teamid, const std::string& name, const receiver_mask& r = receiver_mask());
            bool    Validate(GameController& gc, network::Client::Id sender) const;
            bool    Execute(GameController& game_controller, network::Client::Id sender) const;

        private:
            //BOOST_SERIALIZATION_SPLIT_MEMBER()
            friend class boost::serialization::access;
            template<class Archive>
                void    serialize(Archive & ar, const int unsigned version)
                {
                    ar & m_teamid;
                    ar & m_team_name;
                }
            char                m_teamid;
            std::string         m_team_name;
        };



        class TeamChangeOpenSlots : public GameActionBase
        {
        public:
            TeamChangeOpenSlots();
            TeamChangeOpenSlots(char teamid, char open_slots, const receiver_mask& r = receiver_mask());

            bool    Validate(GameController& gc, network::Client::Id sender) const;
            bool    Execute(GameController& game_controller, network::Client::Id sender) const;

        private:
            //BOOST_SERIALIZATION_SPLIT_MEMBER()
            friend class boost::serialization::access;
            template<class Archive>
                void    serialize(Archive & ar, const int unsigned version)
                {
                    ar & m_teamid;
                    ar & m_open_slots;
                }
            char    m_teamid;
            char    m_open_slots;
        };

    } // namespace action

} // namespace game

#endif // _game_Actions_hpp_
